package cn.wen.java.utils;

import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import org.apache.commons.lang.time.DateUtils;
import org.apache.log4j.Logger;

import cn.wen.java.lang.HelperOfObject;
import cn.wen.java.lang.HelperOfString;
import cn.wen.java.utils.Assert;

/**
 * Title：日期时间工具类
 * <p>
 * Description：格式化样式说明：P1=yyyyMM，P2=yyyy-MM，P3=yyyy/MM，P4=yyyy年MM，P5=yyyy_MM；
 * <p>
 * 
 * <pre>
 *  G 年代标志符 
 *  y 年  
 *  M 月
 *  d 日
 *  h 时 在上午或下 午 (1~12)
 *  H 时 在一天 中 (0~23)  
 *  m 分  
 *  s 秒  
 *  S 毫秒  
 *  E 星期  
 *  D 一年中的第几天  
 *  F 一月中第几个星期几  
 *  w 一年中第几个星期  
 *  W 一月中第几个星期  
 *  a 上午 / 下午 标 记符 
 *  k 时 在一天 中 (1~24)
 *  K 时 在上午或下 午 (0~11)
 *  z 时区
 * 
 * </pre>
 * Copyright：Copyright (c) 2010-2012
 * 
 * @author <a href="mailto:ct_wen@qq.com">ctwen</a>, 温承瑭
 * @version Revision：1.0 Date：2010/7/17 Description：初完成。
 *          <p>
 *          Revision：1.1 Date：2010/11/17 Description：新增样式7、8。
 *          <p>
 *          Revision：1.2 Date：2010/11/19 Description：新增样式9。
 *          <p>
 *          Revision：2.0 Date：2010/11/19 Description：去除之前所有样式方法，新增5种样式，N种日期匹配模式等……
 *          <p>
 *          Revision：2.1 Date：2010/11/19 Description：新增add……，及parse解析方法……
 *          <p>
 *          Revision：2.2 Date：2013/3/2 Description：剔除add……的冗余功能，DateUtils有这些功能。
 * <p>
 * @see org.apache.commons.lang.time.DateUtils
 */
public final class HelperOfDate {
    
    private static final Logger log = Logger.getLogger(HelperOfDate.class);

    // 日期匹配模式
    // -----------------------------------------------------------------------
    /**
     * The format string is <tt>yyyy</tt>
     */
    public final static String P1_Y = "yyyy";
    /**
     * The format string is <tt>yyyyMM</tt>
     */
    public final static String P1_YM = "yyyyMM";
    /**
     * The format string is <tt>yyyyMMdd</tt>
     */
    public final static String P1_YMD = "yyyyMMdd";
    /**
     * The format string is <tt>yyyy-MM</tt>
     */
    public final static String P2_YM = "yyyy-MM";
    /**
     * The format string is <tt>yyyy-MM-dd</tt>
     */
    public final static String P2_YMD = "yyyy-MM-dd";
    /**
     * The format string is <tt>yyyy-MM-dd HH:mm</tt>
     */
    public final static String P2_YMDHM = "yyyy-MM-dd HH:mm";
    /**
     * The format string is <tt>yyyy-MM-dd HH:mm:ss</tt>
     */
    public final static String P2_YMDHMS = "yyyy-MM-dd HH:mm:ss";
    /**
     * The format string is <tt>yyyy-MM-dd HH:mm:ss ms</tt>
     */
    public final static String P2_YMDHMSM = "yyyy-MM-dd HH:mm:ss ms";
    /**
     * The format string is <tt>yyyy/MM</tt>
     */
    public final static String P3_YM = "yyyy/MM";
    /**
     * The format string is <tt>yyyy/MM/dd</tt>
     */
    public final static String P3_YMD = "yyyy/MM/dd";
    /**
     * The format string is <tt>yyyy/MM/dd HH:mm</tt>
     */
    public final static String P3_YMDHM = "yyyy/MM/dd HH:mm";
    /**
     * The format string is <tt>yyyy/MM/dd HH:mm:ss</tt>
     */
    public final static String P3_YMDHMS = "yyyy/MM/dd HH:mm:ss";
    /**
     * The format string is <tt>yyyy/MM/dd HH:mm:ss ms</tt>
     */
    public final static String P3_YMDHMSM = "yyyy/MM/dd HH:mm:ss ms";
    /**
     * The format string is <tt>yyyy年MM月</tt>
     */
    public final static String P4_YM = "yyyy年MM月";
    /**
     * The format string is <tt>yyyy年MM月dd日</tt>
     */
    public final static String P4_YMD = "yyyy年MM月dd日";
    /**
     * The format string is <tt>yyyy年MM月dd日 HH时mm分</tt>
     */
    public final static String P4_YMDHM = "yyyy年MM月dd日 HH时mm分";
    /**
     * The format string is <tt>yyyy年MM月dd日 HH时mm分ss秒</tt>
     */
    public final static String P4_YMDHMS = "yyyy年MM月dd日 HH时mm分ss秒";
    /**
     * The format string is <tt>yyyy_MM_dd_HH_mm_ss</tt>
     */
    public final static String P5_YMDHMS = "yyyy_MM_dd_HH_mm_ss";
    /**
     * The format string is <tt>yyyy_MM_dd_HH_mm_ss_ms</tt>
     */
    public final static String P5_YMDHMSM = "yyyy_MM_dd_HH_mm_ss_ms";

    // 各时间的毫秒数
    // -----------------------------------------------------------------------
    /**
     * Number of milliseconds in a standard second.
     */
    public static final long MILLIS_PER_SECOND = 1000;
    /**
     * Number of milliseconds in a standard minute.
     */
    public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
    /**
     * Number of milliseconds in a standard hour.
     */
    public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
    /**
     * Number of milliseconds in a standard day.
     */
    public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;

    // 格式化
    // -----------------------------------------------------------------------
    /**
     * 获取当前系统日期时间，并转换成指定样式的格式化字符串，自定义样式（Pattern）
     * 
     * @param pattern (reference HelperOfDate.PatternNumber or yyyy_MM_dd_HH_mm_ss_ms)
     * @return 日期格式化字符串
     */
    public static String format(String pattern) {
	return format(Calendar.getInstance().getTime(), pattern);
    }

    /**
     * 将日期时间参数转换成指定样式的格式化字符串，自定义样式（Pattern）
     * 
     * @param time java.util.Date
     * @param pattern (reference HelperOfDate.PatternNumber or yyyy_MM_dd_HH_mm_ss_ms)
     * @return 日期格式化字符串
     */
    public static String format(Date date, String pattern) {
	return new SimpleDateFormat(pattern).format(date);
    }

    // 日期解析
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Parses a string representing a date by trying a variety of different parsers.
     * </p>
     * 
     * <p>
     * The parse will try each parse pattern in turn. A parse is only deemed sucessful if it parses the whole of the input
     * string. If no parse patterns match, return null;
     * </p>
     * 
     * @param dateString the date to parse, not null
     * @param patterns the date format patterns to use, see SimpleDateFormat, not null
     * @return the parsed date
     * @throws IllegalArgumentException if the date string or pattern array is null
     */
    public static Date parse(String dateString, String... patterns) {
	Assert.isNotNull(dateString, "Date and Patterns must not be null");
	Assert.isNotNull(patterns, "Date and Patterns must not be null");

	SimpleDateFormat parser = null;
	ParsePosition pos = new ParsePosition(0);
	for (int i = 0; i < patterns.length; i++) {
	    if (i == 0) {
		parser = new SimpleDateFormat(patterns[0]);
	    }
	    else {
		parser.applyPattern(patterns[i]);
	    }
	    pos.setIndex(0);
	    Date date = parser.parse(dateString, pos);
	    if (date != null && pos.getIndex() == dateString.length()) {
		return date;
	    }
	}

	log.info(HelperOfString.format("Unable to parse the date: {0}, parsePatterns: {1}", dateString,
		HelperOfObject.toString(patterns)));
	return null;
    }

    /**
     * @param dateString
     * @return
     * @see HelperOfDate#parse(String, String...)
     */
    public static Date parse(String dateString) {
	return parse(dateString, P1_YMD, P2_YMD, P3_YMD, P4_YMD, P1_YM, P2_YM, P3_YM, P4_YM);
    }

    // 其他非主要功能
    // -----------------------------------------------------------------------

    /**
     * @return 年度首日。
     */
    public static Date getFirstDayOfYear(Date date) {
	Calendar cale = Calendar.getInstance();
	cale.setTime(date);
	cale.set(Calendar.MONTH, Calendar.JANUARY);
	return getFirstDayOfMonth(cale.getTime());
    }

    /**
     * @return 月度首日。
     */
    public static Date getFirstDayOfMonth(Date date) {
	return DateUtils.truncate(date, Calendar.MONTH);
    }

    /**
     * @return 周度首日。
     */
    public static Date getFirstDayOfWeek(Date date) {
	Calendar cale = Calendar.getInstance();
	cale.setTime(date);
	cale.set(Calendar.DAY_OF_WEEK, 1); // 一周之首
	cale.add(Calendar.DAY_OF_MONTH, 1); // 星期一为首日
	return DateUtils.truncate(cale.getTime(), Calendar.DAY_OF_MONTH);
    }

    /**
     * @return 周度末日。
     */
    public static Date getLastDayOfWeek(Date date) {
	Calendar cale = Calendar.getInstance();
	cale.setTime(getFirstDayOfWeek(date));
	cale.set(Calendar.DAY_OF_WEEK, 7); // 一周之尾
	cale.add(Calendar.DAY_OF_MONTH, 2);
	cale.add(Calendar.MILLISECOND, -1); // 星期日为末日
	return cale.getTime();
    }

    /**
     * @return 月度末日。
     */
    public static Date getLastDayOfMonth(Date date) {
	Calendar cale = Calendar.getInstance();
	cale.setTime(getFirstDayOfMonth(date));
	cale.add(Calendar.MONTH, 1);
	cale.add(Calendar.MILLISECOND, -1);
	return cale.getTime();
    }

    /**
     * @return 年度末日。
     */
    public static Date getLastDayOfYear(Date date) {
	Calendar cale = Calendar.getInstance();
	cale.setTime(getFirstDayOfYear(date));
	cale.add(Calendar.MONTH, 12);
	cale.add(Calendar.MILLISECOND, -1);
	return cale.getTime();
    }

    /**
     * 返回高精度时间的格式化字符串 格式如： {小时}h:{分}m:{秒}s —— {毫秒}ms:{微秒}μs:{纳秒}ns
     * <p>
     * 实例：——计算程序片段用时：<br>
     * //程序片段启动时间<br>
     * long beforeNs = System.nanoTime(); <br>
     * System.out.println("Start time :\t\t"+HelperOfDate.formatPrecisionTime(beforeNs));
     * <p>
     * 
     * //执行程序片段<br>
     * List userList = userDAO.queryAll();<br>
     * for (User user : userList) {<br>
     * System.out.println(user.getLoginName());<br>
     * <p>
     * 
     * //程序片段完成时间<br>
     * long afterNs = System.nanoTime();<br>
     * System.out.println("Done time :\t\t"+HelperOfDate.formatPrecisionTime(afterNs));
     * <p>
     * 
     * //程序片段从启动到完成的时差<br>
     * long nsd = afterNs - beforeNs;<br>
     * System.out.println("Time difference :\t"+HelperOfDate.formatNanoTime(nsd));<br>
     * <p>
     * 
     * @param ns 高精度时间——纳秒（请使用System.nanoTime()获取参数）
     * @return 高精度时间的格式化字符串
     */
    public static String formatNanoTime(long ns) {
	long us = 0, ms = 0, s = 0, m = 0, h = 0;

	// 取微秒
	us = ns / 1000;
	ns -= us * 1000;

	// 取毫秒
	ms = us / 1000;
	us -= ms * 1000;

	// 取秒
	s = ms / 1000;
	ms -= s * 1000;

	// 取分
	m = s / 60;
	s -= m * 60;

	// 取小时
	h = m / 60;
	m -= h * 60;

	// return h + "h:" + m + "m:" + s + "s —— " + ms + "ms:" + us + "μs:" + ns + "ns";
	return HelperOfString.format("{0}h:{1}m:{2}s —— {3}ms:{4}μs:{5}ns", h, m, s, ms, us, ns);
    }
    
    private final static StringOfDay stringOfDay = new StringOfDay();
    
    /**
     * @return 字符串类型的日期操作类（日）
     */
    public static StringOfDay getStringOfDay() {
	return stringOfDay;
    }

    /**
     * Title：字符串类型的日期操作类（日）
     * <p>
     * Description：提供如："20111005"-->yyyyMMdd格式的字符串日期操作。
     * <p>
     * Copyright：Copyright (c) 2011
     * 
     * @author <a href="mailto:ct_wen@qq.com">ctwen</a>, 温承瑭
     * @version Revision：1.0 Date：2011/11/25 Description：初完成。
     *          <p>
     */
    public static class StringOfDay {

	private final String formatPattern = P1_YMD;

	/**
	 * @see HelperOfDate#addYears(Date, int)
	 */
	public String addYears(String dateString, int amount) {
	    return format(DateUtils.addYears(parse(dateString, formatPattern), amount), formatPattern);
	}
	/**
	 * @see HelperOfDate#addMonths(Date, int)
	 */
	public String addMonths(String dateString, int amount) {
	    return format(DateUtils.addMonths(parse(dateString, formatPattern), amount), formatPattern);
	}
	/**
	 * @see HelperOfDate#addWeeks(Date, int)
	 */
	public String addWeeks(String dateString, int amount) {
	    return format(DateUtils.addWeeks(parse(dateString, formatPattern), amount), formatPattern);
	}
	/**
	 * @see HelperOfDate#addDays(Date, int)
	 */
	public String addDays(String dateString, int amount) {
	    return format(DateUtils.addDays(parse(dateString, formatPattern), amount), formatPattern);
	}

    }
    
    private final static StringOfMonth stringOfMonth = new StringOfMonth();

    /**
     * @return 字符串类型的日期操作类（月）
     */
    public static StringOfMonth getStringOfMonth() {
	return stringOfMonth;
    }

    /**
     * Title：字符串类型的日期操作类（月）
     * <p>
     * Description：提供如："201110"-->yyyyMM格式的字符串日期操作。
     * <p>
     * Copyright：Copyright (c) 2011
     * 
     * @author <a href="mailto:ct_wen@qq.com">ctwen</a>, 温承瑭
     * @version Revision：1.0 Date：2011/11/25 Description：初完成。
     *          <p>
     */
    public static class StringOfMonth {

	private final String formatPattern = P1_YM;

	/**
	 * @see HelperOfDate#addMonths(Date, int)
	 */
	public String addMonths(String dateString, int amount) {
	    return format(DateUtils.addMonths(parse(dateString, formatPattern), amount), formatPattern);
	}

    }
    
}
